package cn.touna.jss.modules.third.tcredit;

import cn.touna.risk.api.enums.SystemErrorCodeEnum;
import cn.touna.jss.common.entity.ThreeElementEntity;
import cn.touna.jss.common.exception.ResponseException;
import cn.touna.jss.common.factory.ResponseFactory;
import cn.touna.jss.common.properties.disconf.TcreditProperties;
import cn.touna.jss.common.util.HttpUtil;
import cn.touna.jss.common.util.MD5Util;
import cn.touna.jss.common.util.ValidUtil;
import cn.touna.jss.modules.adapter.ApiMapping;
import cn.touna.jss.modules.custom.cheyiping.dto.MaintenanceRecordRequest;
import cn.touna.jss.modules.custom.cheyiping.utils.CheyipingUtil;
import cn.touna.jss.modules.third.tcredit.entity.TcreditDhbRequest;
import cn.touna.risk.api.domain.Response;
import cn.touna.risk.cache.redis.RedisCache;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestParam;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 天创
 */
@Service
public class TcreditService {

    @Autowired
    private TcreditProperties config;

    @Autowired
    private RedisCache redisCache;

    private static final Logger logger = LogManager.getLogger();

    private String getOrderId(String vin, String plateNo, String engineNo) throws Exception {
        String params = "vin=" + vin;
        if (!StringUtils.isEmpty(plateNo))
            params += ",licensePlate=" + plateNo;
        if (!StringUtils.isEmpty(engineNo))
            params += ",engineNo=" + engineNo;

        String tokenKey = buidTokenKey(config.getGetOrderIdUrl(), config.getTokenId(), params);
        Map<String, String> map = new HashMap<>();
        map.put("appId", config.getAppId());
        map.put("tokenKey", tokenKey);
        map.put("vin", vin);
        map.put("licensePlate", plateNo);
        map.put("engineNo", engineNo);

        String res = HttpUtil.postForm(config.getGetOrderIdUrl(), map);
        logger.info("buyReport result: " + res);
        JSONObject resObj = JSONObject.parseObject(res);
        int status = resObj.getInteger("status");
        if (status == 0) {
            JSONObject data = resObj.getJSONObject("data");
            int result = data.getInteger("result");

            if (result == 1200) {
                return data.getString("orderId");
            } else {
                throw ResponseException.build(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL, data.getString("resultMsg"));
            }
        } else {
            throw ResponseException.build(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL, resObj.getString("message"));
        }
    }

    private Response getOrderId(MaintenanceRecordRequest request) {
        try {
            String orderId = getOrderId(request.getVin(), request.getPlateNo(), request.getEngineNo());
            return ResponseFactory.success(orderId);
        } catch (ResponseException e) {
            return e.getResponse();
        } catch (Exception e) {
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_SYSTEM_ERROR, e.getMessage());
        }
    }

    private String buidTokenKey(String url, String tokenId, String params) throws UnsupportedEncodingException {
        String src = url + tokenId + params;
        return DigestUtils.md5DigestAsHex(src.getBytes("UTF-8"));
    }

    @ApiMapping(value = "tcredit.getReport", desc = "天创.获取维修报告")
    public Response getReport(MaintenanceRecordRequest request) {
        Response response = ValidUtil.valid(request);
        if (response != null)
            return response;
        request.setVin(request.getVin().toUpperCase());
        String key = CheyipingUtil.getRedisKey("tcredit", request);
        String orderId = null;
        try {
            orderId = redisCache.get(key);
        } catch (Exception e) {
            //隔离redis异常
            logger.error(e.getMessage(), e);
        }
        if (StringUtils.isEmpty(orderId)) {
            Response orderIdRes = getOrderId(request);
            if (orderIdRes.checkIfSuccess()) {
                orderId = String.valueOf(orderIdRes.getData());
                redisCache.set(key, orderId, config.getOrderIdCacheDays(), TimeUnit.DAYS);
            } else {
                return orderIdRes;
            }
        }

        logger.info("订单号：" + orderId);
        String params = "orderId=" + orderId;
        String tokenKey = null;
        try {
            tokenKey = buidTokenKey(config.getGetReportUrl(), config.getTokenId(), params);
        } catch (UnsupportedEncodingException e) {
            logger.error(e.getMessage(), e);
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_SYSTEM_ERROR, e.getMessage());
        }
        Map<String, String> map = new HashMap<>();
        map.put("appId", config.getAppId());
        map.put("tokenKey", tokenKey);
        map.put("orderId", orderId);
        String res = null;
        try {
            res = HttpUtil.postForm(config.getGetReportUrl(), map);
            JSONObject resObj = JSONObject.parseObject(res);
            int status = resObj.getInteger("status");
            if (status == 0) {
                JSONObject data = resObj.getJSONObject("data");
                int result = data.getInteger("result");
                if (result == 1205) { //已出报告
                    return ResponseFactory.success(resObj);
                } else if (result == 1204) {
                    Thread.sleep(5000);
                    return getReport(request);
                } else {
                    return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR, data.getString("resultMsg"), resObj);
                }
            } else {
                return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR, resObj.getString("message"), resObj);
            }
        } catch (Exception e) {
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_SYSTEM_ERROR, e.getMessage());
        }
    }

    @ApiMapping(value = "tcredit.vehicleInsuranceRec", desc = "天创.车辆出险详单")
    public Response vehicleInsuranceRec(@RequestParam("plate") String plate, @RequestParam("vin") String vin) {
        if (StringUtils.isEmpty(plate) && StringUtils.isEmpty(vin)) {
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_PARAM_CHECK_FAILED, "plate和vin不能同时为空");
        }
        String params = "";
        if (!StringUtils.isEmpty(plate)) {
            params += "plate=" + plate;
        }
        if (!StringUtils.isEmpty(vin)) {
            if (params.length() > 0) {
                params += ",";
            }
            params += "vin=" + vin;
        }
        String tokenKey = null;
        try {
            tokenKey = buidTokenKey(config.getVehicleInsuranceRecUrl(), config.getTokenId(), params);
        } catch (UnsupportedEncodingException e) {
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_SYSTEM_ERROR, e.getMessage());
        }
        Map<String, String> map = new HashMap<>();
        map.put("appId", config.getAppId());
        map.put("tokenKey", tokenKey);
        if (!StringUtils.isEmpty(plate)) {
            map.put("plate", plate);
        }
        if (!StringUtils.isEmpty("vin")) {
            map.put("vin", vin);
        }
        try {
            String res = null;
            res = HttpUtil.postForm(config.getVehicleInsuranceRecUrl(), map);
            JSONObject resObj = JSONObject.parseObject(res);
            int status = resObj.getInteger("status");
            if (status == 0) {
                return ResponseFactory.success(resObj);
            } else {
                logger.info("failed result: " + res);
                String message = resObj.getString("message");
                return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR, message, resObj);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_CALL_EXTSYSTEM_ERROR, e.getMessage());
        }
    }

    /***
     * 获取兼容性格式报告2
     * @return
     * @throws Exception
     */
    private Object getDhbReport(TcreditDhbRequest request) throws Exception {
        if (StringUtils.isEmpty(request.getMobile()) || StringUtils.isEmpty(request.getTid()))
            throw ResponseException.build(SystemErrorCodeEnum.CRS_PARAM_CHECK_FAILED, "mobile|tid is null");
        Map<String, String> map = new HashMap<>();
        if (request.getInputInfo() != null) {
            map.put("inputInfo", JSON.toJSONString(request.getInputInfo()));
        }
        map.put("mobile", request.getMobile());
        map.put("tid", request.getTid());
        String tokenKey = getTokenKey(map, config.getGetDhbReportUrl(), config.getTokenId());
        map.put("appId", config.getAppId());
        map.put("tokenKey", tokenKey);

        String res = HttpUtil.postForm(config.getGetDhbReportUrl(), map);
        logger.info("getDhbReport result: " + res);
        JSONObject resObj = JSONObject.parseObject(res);
        int status = resObj.getInteger("status");
        if (status == 0) {
            return resObj;
        } else {
            throw ResponseException.build(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL, resObj.getString("message"));
        }
    }

    private Object getCreditScore(ThreeElementEntity request) throws Exception {
        Map<String, String> map = new HashMap<>();
        map.put("idcard", request.getIdCard());
        map.put("name", request.getName());
        map.put("mobile", request.getMobile());
        String bizParam = JSON.toJSONString(map);
        Map<String, String> params = new HashMap<>();
        params.put("param", encode(config.getTokenId2().replaceAll("-", ""), bizParam));
        // 此处传值与接口文档不同，接口文档描述有误
        String tokenKey = getTokenKey(config.getGetCreditScoreUrl(), config.getTokenId2(), params);
        params.put("tokenKey", tokenKey);
        // 注意：appId参数需要在请求的时候作为入参传入，appId不参与MD5摘要
        params.put("appId", config.getAppId2());

        String res = HttpUtil.postForm(config.getGetCreditScoreUrl(), params);
        logger.info("getCreditScore result: " + res);
        JSONObject resObj = JSONObject.parseObject(res);
        int status = resObj.getInteger("status");
        if (status == 0) {
            String data = resObj.getString("data");
            data = decode(config.getTokenId2().replaceAll("-", ""), data, "UTF-8");
            resObj.put("data", JSONObject.parseObject(data));
            logger.info("getCreditScore result: " + resObj.toJSONString());
            return resObj;
        } else {
            throw ResponseException.build(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL, resObj.getString("message"));
        }
    }

    /**
     * 无间探针
     * @param request
     * @return
     * @throws Exception
     */
    private Object getInfernalProbe(ThreeElementEntity request) throws Exception {
        Map<String, String> map = new HashMap<>();
        map.put("idcard", request.getIdCard());
        map.put("name", request.getName());
        map.put("mobile", request.getMobile());
        String bizParam = JSON.toJSONString(map);
        Map<String, String> params = new HashMap<>();
        params.put("param", encode(config.getTokenId2().replaceAll("-", ""), bizParam));
        // 此处传值与接口文档不同，接口文档描述有误
        String tokenKey = getTokenKey(config.getGetInfernalProbeUrl(), config.getTokenId2(), params);
        params.put("tokenKey", tokenKey);
        // 注意：appId参数需要在请求的时候作为入参传入，appId不参与MD5摘要
        params.put("appId", config.getAppId2());

        String res = HttpUtil.postForm(config.getGetInfernalProbeUrl(), params);
        logger.info("getCreditScore result: " + res);
        JSONObject resObj = JSONObject.parseObject(res);
        int status = resObj.getInteger("status");
        if (status == 0) {
            String data = resObj.getString("data");
            data = decode(config.getTokenId2().replaceAll("-", ""), data, "UTF-8");
            resObj.put("data", JSONObject.parseObject(data));
            logger.info("getCreditScore result: " + resObj.toJSONString());
            return resObj;
        } else {
            throw ResponseException.build(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL, resObj.getString("message"));
        }
    }


    /* 名称: getTokenKey
     * 作者：陈祥
     * 日期：2017年10月11日 下午2:23:36
            * 描述: 生成TokenKey
     * 参数： url-地址  tokenId-客户的tokenId  param-参数
     * 返回值： String 返回的tokenKey
     * 异常：
    */
    public static String getTokenKey(String url, String tokenId, Map<String, String> params) {
        String paramStr = sortParam(params);
        return md5Hex(url + tokenId + paramStr);
    }

    /**
     * 名称: sortParam
     * 作者：陈祥
     * 日期：2017年10月11日 下午2:25:12
     * 描述: 生成参数字符串，参数key按字典序排列
     * 参数： param-生成token需要的参数
     * 返回值： String
     * 异常：
     */
    public static String sortParam(Map<String, String> param) {
        if (null == param || 0 == param.size()) {
            return "";
        }
        // 排序键，按照字母先后进行排序
        Iterator<String> iterator = param.keySet().iterator();
        String[] arr = new String[param.size()];
        for (int i = 0; iterator.hasNext(); i++) {
            arr[i] = iterator.next();
        }
        Arrays.sort(arr);
        // 生成进行MD5摘要的字符串
        StringBuffer sb = new StringBuffer();
        for (String key : arr) {
            String value = param.get(key);
            if (!StringUtils.isEmpty(value)) {
                sb.append(key).append("=").append(value).append(",");
            }
        }
        // 检查结果
        if (sb.length() > 0) {
            return sb.substring(0, sb.length() - 1);
        } else {
            return "";
        }
    }

    /**
     * 名称: md5Hex
     * 作者：陈祥
     * 日期：2017年10月11日 下午2:47:44
     * 描述: 对字符串进行md5摘要，然后转化成16进制字符串
     * 使用标准的md5摘要算法
     * 参数： text-需要进行摘要的字符串
     * 返回值： 进行MD5摘要以及16进制转化后的字符串
     */
    public static String md5Hex(String text) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] bytes = md5.digest(text.trim().getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < bytes.length; i++) {
                int high = (bytes[i] >> 4) & 0x0f;
                int low = bytes[i] & 0x0f;
                sb.append(high > 9 ? (char) ((high - 10) + 'a') : (char) (high + '0'));
                sb.append(low > 9 ? (char) ((low - 10) + 'a') : (char) (low + '0'));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            System.out.println("系统不支持MD5算法");
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            System.out.println("系统不支持指定的编码格式");
            e.printStackTrace();
        }
        return null;
    }


    public String getTokenKey(Map<String, String> param, String url, String tokenId) {
        logger.debug(JSON.toJSONString(param) + url + tokenId);
        // 排序键
        Iterator<String> iterator = param.keySet().iterator();
        String[] arr = new String[param.size()];
        for (int i = 0; iterator.hasNext(); i++) {
            arr[i] = iterator.next();
        }
        Arrays.sort(arr);

        // 生成字符串
        StringBuffer sb = new StringBuffer();
        for (String key : arr) {
            String value = param.get(key);
            if (org.apache.commons.lang3.StringUtils.isNotBlank(value)) {
                sb.append(key).append("=").append(value).append(",");
            }
        }
        String paramStr = sb.length() > 0 ? sb.substring(0, sb.length() - 1) : "";

        //拼接字符串并返回MD5值
        return MD5Util.MD5(url + tokenId + paramStr);
    }

    @ApiMapping(value = "tcredit.getDhbReport", desc = "天创.用户信息报告")
    public Response findDhbReport(TcreditDhbRequest request) {
        try {
            Response response = ValidUtil.valid(request);
            if (response != null)
                return response;
            Object data = getDhbReport(request);
            return ResponseFactory.success(data);
        } catch (ResponseException e) {
            return e.getResponse();
        } catch (Exception e) {
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_SYSTEM_ERROR, e.getMessage());
        }
    }

    @ApiMapping(value = "tcredit.getCreditScore", desc = "天创.芝麻分")
    public Response findCreditScore(ThreeElementEntity request) {
        try {
            Response response = ValidUtil.valid(request);
            if (response != null)
                return response;
            Object data = getCreditScore(request);
            return ResponseFactory.success(data);
        } catch (ResponseException e) {
            return e.getResponse();
        } catch (Exception e) {
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_SYSTEM_ERROR, e.getMessage());
        }
    }

    @ApiMapping(value = "tcredit.getInfernalProbe",desc = "天创.无间探针")
    public Response findInfernalProbe(ThreeElementEntity request) {
        try {
            Response response = ValidUtil.valid(request);
            if (response != null)
                return response;
            Object data = getInfernalProbe(request);
            return ResponseFactory.success(data);
        } catch (ResponseException e) {
            return e.getResponse();
        } catch (Exception e) {
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_SYSTEM_ERROR, e.getMessage());
        }
    }

    /**
     * 加密
     *
     * @param key  需要加密的业务类型
     *             128｜196｜256位密钥
     * @param data 待加密的字符串
     * @return 密文
     */
    public static String encode(String key, String data) {
        try {
            SecretKey secretKey = new SecretKeySpec(hex2bin(key), "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            byte[] bytes = cipher.doFinal(data.getBytes());
            return bin2hex(bytes);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 加密
     *
     * @param key  128｜196｜256位密钥
     * @param data 明文
     * @return 密文
     */
    public static String encode(String key, String data, String charset) {
        try {
            SecretKey secretKey = new SecretKeySpec(hex2bin(key), "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            byte[] bytes = cipher.doFinal(data.getBytes(charset));
            return bin2hex(bytes);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 解密
     *
     * @param key  128｜196｜256位密钥
     * @param data 密文
     * @return 明文
     */
    public static String decode(String key, String data, String charset) {
        try {
            SecretKey secretKey = new SecretKeySpec(hex2bin(key), "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            byte[] bytes = cipher.doFinal(hex2bin(data));
            return new String(bytes, charset);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将2进制转换成16进制
     *
     * @param bin 2进制数组
     * @return 16进制字符串
     */
    private static String bin2hex(byte[] bin) throws Exception {
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < bin.length; i++) {
            String hex = Integer.toHexString(bin[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            buf.append(hex.toUpperCase());
        }
        return buf.toString();
    }

    /**
     * 将16进制转换为2进制
     *
     * @param hex 16进制字符串
     * @return 2进制数组
     */
    private static byte[] hex2bin(String hex) throws Exception {
        if (hex.length() < 1)
            return null;
        byte[] result = new byte[hex.length() / 2];
        for (int i = 0; i < hex.length() / 2; i++) {
            int high = Integer.parseInt(hex.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hex.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }

    /**
     * 随机生成秘钥
     */
    public static void getkey() {
        try {
            KeyGenerator kg = KeyGenerator.getInstance("AES");
            kg.init(128);
            //要生成多少位，只需要修改这里即可128, 192或256
            SecretKey sk = kg.generateKey();
            byte[] b = sk.getEncoded();
            String s = bin2hex(b);
            System.out.println(s);
            System.out.println("十六进制密钥长度为" + s.length());
            System.out.println("二进制密钥的长度为" + s.length() * 4);
        } catch (Exception e) {
            e.printStackTrace();

        }
    }

}
